package com.webdev.base.controller;


import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.json.JSONUtil;
import com.webdev.base.pojo.Orders;
import com.webdev.base.pojo.User;
import com.webdev.base.rabbitmq.MQSender;
import com.webdev.base.rabbitmq.SeckillMessage;
import com.webdev.base.service.IGoodsService;
import com.webdev.base.util.Constants;
import com.webdev.base.vo.GoodsVo;
import com.webdev.base.vo.RespBean;
import com.webdev.base.vo.RespBeanEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.awt.event.WindowFocusListener;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * <p>
 * 前端控制器
 * </p>
 *
 * @author huangningshuang
 * @since 2021-12-13
 */
@Controller
@Slf4j
@RequestMapping("/seckill")
public class OrderController implements InitializingBean {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private IGoodsService goodsService;

    @Autowired
    private MQSender mqSender;

    //内存标记，标记商品数量小于0的，减少与redis的交互
    private final Map<Integer, Boolean> emptyStockMap = new HashMap<>();

    /**
     * 前端轮询接口，1表示秒杀成功，0表示排队中，-1表示秒杀失败
     *
     * @param user
     * @param goodsId
     * @return
     */
    @GetMapping("/getResult")
    @ResponseBody
    public RespBean getResult(User user, Integer goodsId) {
        if (user == null) {
            return RespBean.error(RespBeanEnum.SESSION_ERROR);
        }
        Orders order = (Orders) redisTemplate.opsForValue().get(Constants.REDIS_ORDERKEY + user.getUserId());
        if (order != null) {
            return RespBean.success(1);
        } else if (Boolean.TRUE.equals(redisTemplate.hasKey(Constants.REDIS_EMPTYGOODSKEY + goodsId))) {
            return RespBean.success(-1);
        } else {
            return RespBean.success(0);
        }
    }

    @RequestMapping("/orders")
    @ResponseBody
    public RespBean queryOrders(User user) {
        if (user == null) {
            return RespBean.error(RespBeanEnum.SESSION_ERROR);
        }
        Orders order = (Orders) redisTemplate.opsForValue().get(Constants.REDIS_ORDERKEY + user.getUserId());
        if (order != null) {
            GoodsVo goodsVo = goodsService.findGoodsVoByGoodId(order.getOrderGoodsId());
            return RespBean.success(goodsVo);
        }
        return RespBean.success();
    }

    private final Lock redisSetLock = new ReentrantLock();

    @RequestMapping("/doSecKill")
    @ResponseBody
    public RespBean doSecKill(User user, Integer goodsId) throws ParseException {
        if (user == null) {
            return RespBean.error(RespBeanEnum.USER_NOT_EXIST);
        }
        log.info("userinfo: " + user.getUserName());
        //在秒杀开始前或者后不能进行秒杀
        Date start = (Date) redisTemplate.opsForValue().get(Constants.REDIS_GOODSSTARTKEY + goodsId);
        Date end = (Date) redisTemplate.opsForValue().get(Constants.REDIS_GOODSENDKEY + goodsId);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        sdf.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai")); // 设置北京时区
        Date cur = sdf.parse(sdf.format(new Date()));
        log.info(start + " : " + cur + ":" + end);
        if (cur.before(start)) {
            return RespBean.error(RespBeanEnum.BEFORE_SECKILL);
        }
        if (cur.after(end)) {
            return RespBean.error(RespBeanEnum.AFTER_SECKILL);
        }
        //判断用户是否重复购买
        Orders order = (Orders) redisTemplate.opsForValue().get(Constants.REDIS_ORDERKEY + user.getUserId());
        if (order != null) {
            return RespBean.error(RespBeanEnum.REPEATE_ERROR);
        }
        //内存标记，减少redis交互
        if (emptyStockMap.get(goodsId)) {
            return RespBean.error(RespBeanEnum.EMPTY_STOCK);
        }
        //防止同一用户同一时间的大量请求
        if (Boolean.FALSE.equals(redisTemplate.hasKey(Constants.REDIS_HANDLINGUSER + user.getUserId()))) {
            redisSetLock.lock();
            try {
                if (Boolean.FALSE.equals(redisTemplate.hasKey(Constants.REDIS_HANDLINGUSER + user.getUserId()))) {
                    redisTemplate.opsForValue().set(Constants.REDIS_HANDLINGUSER + user.getUserId(), true);
                } else {
                    return RespBean.error(RespBeanEnum.HANDLING);
                }
            } finally {
                redisSetLock.unlock();
            }
        } else {
            return RespBean.error(RespBeanEnum.HANDLING);
        }

        //redis预减
        Long count = redisTemplate.opsForValue().decrement(Constants.REDIS_GOODSKEY + goodsId);
        if (count == null || count < 0) {
            emptyStockMap.put(goodsId, true);
            redisTemplate.delete(Constants.REDIS_HANDLINGUSER + user.getUserId());
            redisTemplate.opsForValue().increment(Constants.REDIS_GOODSKEY + goodsId);
            return RespBean.error(RespBeanEnum.EMPTY_STOCK);
        }

        SeckillMessage msg = new SeckillMessage(user, goodsId);
        mqSender.sendOrder(JSONUtil.toJsonStr(msg));
        return RespBean.success(0);
    }

    /**
     * 初始化执行的方法，将商品加载到redis中
     */
    @Override
    public void afterPropertiesSet() {
        List<GoodsVo> list = goodsService.findGoodsVoList();
        if (CollectionUtil.isEmpty(list)) {
            return;
        }

        list.forEach(goodsVo -> {
            redisTemplate.opsForValue().set(Constants.REDIS_GOODSKEY + goodsVo.getGoodsId(), goodsVo.getGoodsCount());
            redisTemplate.opsForValue().set(Constants.REDIS_GOODSSTARTKEY + goodsVo.getGoodsId(), goodsVo.getStartDate());
            redisTemplate.opsForValue().set(Constants.REDIS_GOODSENDKEY + goodsVo.getGoodsId(), goodsVo.getEndDate());
            if (goodsVo.getGoodsCount() > 0) {
                emptyStockMap.put(goodsVo.getGoodsId(), false);
            } else {
                emptyStockMap.put(goodsVo.getGoodsId(), true);
            }
        });
    }

}
